package com.wiscamp.ninechapters.problems.domain.services;


import com.wiscamp.ninechapters.problems.domain.ProblemsErrorCodes;
import com.wiscamp.ninechapters.problems.domain.models.problems.problems.CompositeProblem;
import com.wiscamp.ninechapters.problems.domain.models.problems.problems.HybridProblem;
import com.wiscamp.ninechapters.problems.domain.models.problems.problems.Problem;
import com.wiscamp.ninechapters.problems.domain.models.problems.problems.SimpleProblem;
import com.wiscamp.ninechapters.problems.domain.repositories.ProblemCollectionRepository;
import com.wiscamp.ninechapters.problems.domain.repositories.ProblemRepository;
import com.wiscamp.ninechapters.problems.enums.AnswerTypes;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.domain.BaseDomainService;
import cube.ddd.exception.ExceptionUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

/**
 * 题领域服务
 */
@Service
public class ProblemService extends BaseDomainService {

    private final ProblemRepository problemRepository;
    private final ProblemCollectionRepository problemCollectionRepository;

    private final String CACHE_NAME = "problems";

    public ProblemService(ProblemRepository problemRepository, ProblemCollectionRepository problemCollectionRepository) {
        this.problemRepository = problemRepository;
        this.problemCollectionRepository = problemCollectionRepository;
    }


    // region problem service

    /**
     * 录入新题时根据传入的答案内容解析答案类型，主要用于兼容旧逻辑
     *
     * @param contentId   答案内容编号
     * @param contentText 答案内容文本
     * @return return the id of answer type
     * @deprecated 兼容老前端逻辑，编辑题目保存后主动填入AnswerTypeEnum，前端升级后移除此逻辑
     */
    @Deprecated(forRemoval = true)
    public static int preprocessAnswerType(long contentId, String contentText) {
        if (contentId > 0) {
            return AnswerTypes.OPTION.getValue();
        } else {
            if (contentText.contains(","))
                return AnswerTypes.MULTIPLE_OPTION.getValue();
            else if (contentText.contains("."))
                return AnswerTypes.DECIMAL.getValue();
            else if (contentText.contains("\\frac"))
                return AnswerTypes.FRACTION.getValue();
            else if (contentText.contains("\\sqrt"))
                return AnswerTypes.RADICAL_NUMBER.getValue();
            else if (contentText.contains("TRUE") || contentText.contains("FALSE"))
                return AnswerTypes.BOOLEAN.getValue();
            else if (contentText.contains("-"))
                return AnswerTypes.INTEGER_RANGE.getValue();
        }
        return AnswerTypes.INTEGER.getValue();
    }

    @Cacheable(value = CACHE_NAME, key = "#problemId")
    @HandleServiceException
    public Problem getProblem(long problemId) {
        var problem = problemRepository.getProblem(problemId);
        if (Objects.isNull(problem)) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.PROBLEM_NOT_FOUND, problemId);
        }
        return problem;
    }

    /**
     * Creates a item
     *
     * @param item
     * @return
     */
    @CachePut(value = CACHE_NAME, key = "#problem.problemId")
    @Transactional(transactionManager = "problemsTransactionManager")
    @HandleServiceException
    public Problem createProblem(@NotNull Problem problem, @Min(0) long operatorId) {
        if (problem instanceof SimpleProblem)
            return createSimpleProblem((SimpleProblem) problem, operatorId);

        if (problem instanceof CompositeProblem)
            return createCompositeProblem((CompositeProblem) problem, operatorId);

        if (problem instanceof HybridProblem)
            return createHybridProblem((HybridProblem) problem, operatorId);

        return null;
    }

    /**
     * Updates a item
     *
     * @param item
     * @return
     */
    @CachePut(value = CACHE_NAME, key = "#problem.problemId")
    // @Transactional
    @HandleServiceException
    public Problem updateProblem(@NotNull Problem problem, @Min(0) long operatorId) {
        if (problem instanceof SimpleProblem) {
            updateSimpleProblem((SimpleProblem) problem, operatorId);
        }

        if (problem instanceof CompositeProblem) {
            updateCompositeProblem((CompositeProblem) problem, operatorId);
        }

        if (problem instanceof HybridProblem) {
            updateHybridProblem((HybridProblem) problem, operatorId);
        }

        return getProblem(problem.getProblemId());
    }

    /**
     * Logically deletes a item
     *
     * @param problemId
     * @return
     */
    @CacheEvict(value = CACHE_NAME, key = "#problemId")
    @HandleServiceException
    public boolean deleteProblem(@Min(0) long problemId, @Min(0) long operatorId) {
        var problem = getProblem(problemId);
        if (Objects.isNull(problem)) return false;
        // 记录操作日志
        problem.delete();
        problem.updateBy(operatorId);
        return problemRepository.updateProblem(problem);
    }

    // endregion

    // region problems
    @HandleServiceException
    public Collection<Problem> getProblemsByIds(Collection<Long> problemIds) {
        // return problemRepository.getProblemsByIds(problemIds);
        var problems = new ArrayList<Problem>();
        for (long problemId : problemIds) {
            problems.add(getProblem(problemId));
        }
        return problems;
    }
    // endregion

    // region hybrid problem
    private Problem createHybridProblem(@NotNull HybridProblem hybridProblem, @Min(0) long operatorId) {
        hybridProblem.createBy(operatorId);
        return problemRepository.createHybridProblem(hybridProblem);
    }

    private void updateHybridProblem(@NotNull HybridProblem hybridProblem, @Min(0) long operatorId) {
        hybridProblem.updateBy(operatorId);
        problemRepository.updateHybridProblem(hybridProblem);
    }

    // endregion

    // region composite problem

    private Problem createCompositeProblem(@NotNull CompositeProblem compositeProblem, @Min(0) long operatorId) {
        compositeProblem.createBy(operatorId);
        return problemRepository.createCompositeProblem(compositeProblem);
    }

    private void updateCompositeProblem(@NotNull CompositeProblem compositeProblem, @Min(0) long operatorId) {
        compositeProblem.updateBy(operatorId);
        problemRepository.updateCompositeProblem(compositeProblem);
    }
    // endregion

    // region simple problem
    public SimpleProblem createSimpleProblem(@NotNull SimpleProblem simpleProblem, @Min(0) long operatorId) {
        simpleProblem.createBy(operatorId);
        return problemRepository.createSimpleProblem(simpleProblem);
    }

    public void updateSimpleProblem(SimpleProblem simpleProblem, @Min(0) long operatorId) {
        simpleProblem.updateBy(operatorId);
        problemRepository.updateSimpleProblem(simpleProblem);
    }
    // endregion

    // region get problemIds

    /*@HandleServiceException
    public Collection<Long> getProblemIdsByTagAndDifficulty(@NotEmpty Collection<Long> tagIds, @Min(0) int startDifficulty,
                                                         @Min(0) int endDifficulty) {

        return problemRepository.getProblemIdsByTagAndDifficulty(tagIds, startDifficulty, endDifficulty);
    }

    @HandleServiceException
    public Collection<Long> getProblemIdsByPointAndDifficulty(@NotEmpty Collection<Long> pointIds, @Min(0) int startDifficulty,
                                                           @Min(0) int endDifficulty) {
        return problemRepository.getProblemIdsByPointAndDifficulty(pointIds, startDifficulty, endDifficulty);
    }*/

    // endregion

    // region sorted problems

    /*@HandleServiceException
    public Collection<Problem> getSortedProblemsByDifficulty(@NotEmpty Collection<Long> problemIds) {
        return problemRepository.getSortedProblemsByDifficulty(problemIds);
    }

    @HandleServiceException
    public Collection<Long> getSortedProblemIdsByDifficulty(@NotEmpty Collection<Long> problemIds) {
        return problemRepository.getSortedProblemIdsByDifficulty(problemIds);
    }*/

    // endregion

    // region exams
    /*public long getExamIdByQuestion(@NotNull Question question) {
        long problemId = question.getProblemId();
        var problem = getProblem(problemId);
        var pool = problem.getProblemCollection();
        if (Objects.isNull(pool)) return 0;
        return pool.getExamId();
    }*/
    // endregion

    // region tags
    /*@Cacheable(value = "problem:tagNames", key = "#problemId")
    @HandleServiceException
    public Collection<TagName> getTagNamesByProblem(long problemId) {
        return problemRepository.getTagNamesByProblemId(problemId);
    }*/

    // endregion
}
